﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.IO;
using System.Data;
using System.Xml.XPath;
using System.Collections;
using System.Net;

namespace CNVP.Framework.Utils
{
    public class XmlUtils
    {
        private XmlDocument m_xmlDocument;
        private XPathNavigator m_nav;
        private string m_sLastErrorMessage;
        /// <summary>
        /// 加载方式
        /// </summary>
        public enum LoadType { 
            /// <summary>
            /// 直接加载XML文本
            /// </summary>
            FromString, 
            /// <summary>
            /// 从文件中加载XML
            /// </summary>
            FromLocalFile, 
            /// <summary>
            /// 从URL中加载XML
            /// </summary>
            FromURL }

        public XmlUtils()
        {
            m_sLastErrorMessage = "";
            m_xmlDocument = new XmlDocument();            
        }
        /// <summary>
        /// 最后一条错误信息
        /// </summary>
        public string LastErrorMessage
        {
            get { return m_sLastErrorMessage; }
            set { m_sLastErrorMessage = value; }
        }
        /// <summary>
        /// 获取根节点
        /// </summary>
        public XmlNode RootNode
        {
            get { return m_xmlDocument.DocumentElement; }
        }
        /// <summary>
        /// 获取XML文档对象
        /// </summary>
        public XmlDocument Document
        {
            get { return m_xmlDocument; }
        }
        /// <summary>
        /// 获取XML导航对象
        /// </summary>
        public XPathNavigator Navigator
        {
            get { return m_nav; }
        }
        /// <summary>
        /// 保存事件，以适应更多复杂的保存操作
        /// </summary>
        /// <param name="sTargetXML">保存目标</param>
        /// <returns>成功为True  否则为False</returns>
        public delegate bool Save(string sTargetXML);

        /// <summary>
        /// 保存XML到指定文件
        /// </summary>
        /// <param name="sTargetFileName">指定文件</param>
        /// <returns>成功为True  否则为False</returns>
        public bool SaveToFile(string sTargetFileName)
        {
            bool bResult = false;

            try
            {
                m_xmlDocument.Save(sTargetFileName);
                bResult = true;
            }
            catch (XmlException e)
            {
                HandleException(e);
            }

            return bResult;
        }

        /// <summary>        
        /// 将所有XML转成字符串输出
        /// </summary>
        public override string ToString()
        {
            return m_xmlDocument.OuterXml;
        }

        private void DoPostLoadCreateInit()
        {
            m_nav = m_xmlDocument.CreateNavigator();
            MoveToRoot();
        }

        /// <summary>
        /// 加载XML数据，可以从文本、文件或者URL加载
        /// </summary>
        /// <param name="sourceXMLOrFile">文本、文件或者URL</param>
        /// <param name="loadType">加载类型</param>
        /// <returns>成功为True  否则为False</returns>
        public bool LoadXML(string sourceXMLOrFile, LoadType loadType)
        {
            bool bLoadResult = false;

            try
            {
                switch (loadType)
                {
                    case XmlUtils.LoadType.FromString:
                        m_xmlDocument.LoadXml(sourceXMLOrFile); // 从文本中加载
                        break;
                    case XmlUtils.LoadType.FromLocalFile:
                        m_xmlDocument.Load(sourceXMLOrFile); // 从文件中加载
                        break;

                    case XmlUtils.LoadType.FromURL:        // 从URL中加载
                        {
                            string sURLContent = GetURLContent(sourceXMLOrFile);
                            m_xmlDocument.LoadXml(sURLContent);
                            break;
                        }

                    default:
                        string sErr = "错误：没有与  " + loadType.ToString()+" 相对应的加载类型";
                        throw (new Exception(sErr));
                }
                DoPostLoadCreateInit();

                bLoadResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bLoadResult;
        }

        /// <summary>
        /// 从指定的URL里获取XML文本
        /// </summary>
        /// <param name="sURL">URL</param>
        /// <returns>XML文本</returns>
        public string GetURLContent(string sURL)
        {
            string s = "";
            try
            {
                HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create(sURL);
                HttpWebResponse webresp = (HttpWebResponse)webreq.GetResponse();

                StreamReader stream = new StreamReader(webresp.GetResponseStream(), Encoding.UTF8);
                s = stream.ReadToEnd();
                stream.Close();
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return s;
        }
        /// <summary>
        /// 移动到根节点
        /// </summary>
        /// <returns>成功为True  否则为False</returns>
        public bool MoveToRoot()
        {
            bool bResult = false;
            try
            {
                m_nav.MoveToRoot(); // go to root node!
                bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bResult;
        }

        /// <summary>
        /// 以ArrayList返回指定的XML节点
        /// </summary>
        /// <param name="xPathExpression">XPath表达式</param>
        /// <returns>ArrayList</returns>
        public ArrayList GetChildNodesFromCriteria(string xPathExpression)
        {
            ArrayList al = new ArrayList();
            try
            {
                XmlNodeList nl = m_xmlDocument.SelectNodes(xPathExpression);

                if (nl != null)
                {
                    for (int i = 0; i < nl.Count; i++)
                        al.Add(nl.Item(i));
                }
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return al;
        }
        /// <summary>
        /// 获取指定XPath的首节点
        /// </summary>
        /// <param name="xPathExpression">XPath表达式</param>
        /// <returns>首节点</returns>
        public XmlNode GetFirstChildNodeFromCriteria(string xPathExpression)
        {

            XmlNode node = null;

            try
            {
                node = m_xmlDocument.SelectSingleNode(xPathExpression);
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return node;
        }

        /// <summary>
        /// 获取指定节点的属性的值
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="sAttributeName">属性</param>
        /// <returns>属性的值</returns>
        public string GetAttributeValue(XmlNode node, string sAttributeName)
        {
            string sVal = "";
            try
            {
                XmlAttributeCollection attribColl = node.Attributes;
                XmlAttribute attrib = attribColl[sAttributeName, ""];
                sVal = Decode(attrib.Value);
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return sVal;
        }
        /// <summary>
        /// 以Int32类型返回指定节点的属性的值
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="sAttributeName">属性</param>
        /// <returns>属性的值</returns>
        public int GetAttributeInt32Value(XmlNode node, string sAttributeName)
        {
            string sVal = GetAttributeValue(node, sAttributeName);

            return sVal != "" ? Convert.ToInt32(sVal) : 0;
        }

        /// <summary>
        /// 以Float类型返回指定节点的属性的值
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="sAttributeName">属性</param>
        /// <returns>属性的值</returns>
        public float GetAttributeFloatValue(XmlNode node, string sAttributeName)
        {
            string sVal = GetAttributeValue(node, sAttributeName);
            return sVal != "" ? Convert.ToSingle(sVal) : 0;
        }
        /// <summary>
        /// 以Double类型返回指定节点的属性的值
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="sAttributeName">属性</param>
        /// <returns>属性的值</returns>
        public double GetAttributeDoubleValue(XmlNode node, string sAttributeName)
        {
            string sVal = GetAttributeValue(node, sAttributeName);
            return sVal != "" ? Convert.ToDouble(sVal) : 0.00;
        }
        /// <summary>
        /// 以Boolean类型返回指定节点的属性的值
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="sAttributeName">属性</param>
        /// <returns>属性的值</returns>
        public bool GetAttributeBooleanValue(XmlNode node, string sAttributeName)
        {
            string sVal = GetAttributeValue(node, sAttributeName);
            return sVal != "" ? Convert.ToBoolean(sVal) : false;
        }
        /// <summary>
        /// 获取指定节点的元素的值
        /// </summary>
        /// <param name="xmlNode">节点</param>
        /// <returns>元素的值</returns>
        public string GetElementValue(XmlNode xmlNode)
        {
            string sVal = "";

            try
            {
                sVal = Decode(xmlNode.InnerXml);
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return sVal;
        }
        /// <summary>
        /// 以Int32类型返回指定节点的元素的值
        /// </summary>
        /// <param name="xmlNode">节点</param>
        /// <returns>元素的值</returns>
        public int GetElementInt32Value(XmlNode xmlNode)
        {
            string sVal = GetElementValue(xmlNode);
            return sVal != "" ? Convert.ToInt32(sVal) : 0;
        }
        /// <summary>
        /// 以Float类型返回指定节点的元素的值
        /// </summary>
        /// <param name="xmlNode">节点</param>
        /// <returns>元素的值</returns>
        public float GetElementFloatValue(XmlNode xmlNode)
        {
            string sVal = GetElementValue(xmlNode);
            return sVal != "" ? Convert.ToSingle(sVal) : 0;
        }
        /// <summary>
        /// 以Double类型返回指定节点的元素的值
        /// </summary>
        /// <param name="xmlNode">节点</param>
        /// <returns>元素的值</returns>
        public double GetElementDoubleValue(XmlNode xmlNode)
        {
            string sVal = GetElementValue(xmlNode);
            return sVal != "" ? Convert.ToDouble(sVal) : 0.00;
        }
        /// <summary>
        /// 以Boolean类型返回指定节点的元素的值
        /// </summary>
        /// <param name="xmlNode">节点</param>
        /// <returns>元素的值</returns>
        public bool GetElementBooleanValue(XmlNode xmlNode)
        {
            string sVal = GetElementValue(xmlNode);
            return sVal != "" ? Convert.ToBoolean(sVal) : false;
        }
        /// <summary>        
        /// 获取节点列表里指定元素的节点
        /// </summary>
        /// <param name="nodeList">节点列表</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>节点</returns>
        public XmlNode GetChildElement(XmlNodeList nodeList, string sElementName)
        {
            try
            {
                foreach (XmlNode childNode in nodeList)
                {
                    if (childNode.Name == sElementName)
                    {
                        return childNode;
                    }
                }
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return null;
        }
        /// <summary>        
        /// 获取子节点下指定元素的值
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素的值</returns>
        public string GetChildElementValue(XmlNode parentNode, string sElementName)
        {
            string sVal = "";
            try
            {
                XmlNodeList childNodes = parentNode.ChildNodes;
                foreach (XmlNode childNode in childNodes)
                {
                    if (childNode.Name == sElementName)
                    {
                        sVal = GetElementValue(childNode);
                        break;
                    }
                }
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return sVal;
        }
        /// <summary>
        /// 以Int32返回子节点下元素的值
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素的值</returns>
        public int GetChildElementInt32Value(XmlNode parentNode, string sElementName)
        {
            string sVal = GetChildElementValue(parentNode, sElementName);
            return sVal != "" ? Convert.ToInt32(sVal) : 0;
        }
        /// <summary>
        /// 以Float返回子节点下元素的值
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素的值</returns>
        public float GetChildElementFloatValue(XmlNode parentNode, string sElementName)
        {
            string sVal = GetChildElementValue(parentNode, sElementName);
            return sVal != "" ? Convert.ToSingle(sVal) : 0;
        }
        /// <summary>
        /// 以Double返回子节点下元素的值
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素的值</returns>
        public double GetChildElementDoubleValue(XmlNode parentNode, string sElementName)
        {
            string sVal = GetChildElementValue(parentNode, sElementName);
            return sVal != "" ? Convert.ToDouble(sVal) : 0.00;
        }
        /// <summary>
        /// 以Boolean返回子节点下元素的值
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素的值</returns>
        public bool GetChildElementBooleanValue(XmlNode parentNode, string sElementName)
        {
            string sVal = GetChildElementValue(parentNode, sElementName);
            return sVal != "" ? Convert.ToBoolean(sVal) : false;
        }
        /// <summary>
        /// 获取根节点下的所有指定元素的首元素值
        /// </summary>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素值</returns>
        public XmlNode GetFirstChildXmlNodeFromRoot(string sElementName)
        {
            XmlNodeList nodeList = GetChildNodesFromRoot(sElementName);
            if (nodeList.Count > 0)
                return nodeList[0];

            return null;
        }

        /// <summary>
        /// 获取首个子节点下的元素值
        /// 如果是根节点，则不能用此方法，可以使用 GetFirstChildXmlNodeFromRoot
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素值</returns>
        public XmlNode GetFirstChildXmlNode(XmlNode parentNode, string sElementName)
        {
            XmlNode foundChildNode = null;
            try
            {
                XmlNodeList childNodes = parentNode.ChildNodes;
                foreach (XmlNode childNode in childNodes)
                {
                    if (childNode.Name == sElementName)
                    {
                        foundChildNode = childNode;
                        break;
                    }
                }
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return foundChildNode;
        }
        /// <summary>
        /// 返回根节点下指定元素的节点列表
        /// </summary>
        /// <param name="sElementName">元素名</param>
        /// <returns>节点列表</returns>
        public XmlNodeList GetChildNodesFromRoot(string sElementName)
        {
            return m_xmlDocument.GetElementsByTagName(sElementName);
        }

        /// <summary>
        /// 以ArrayList返回所有子节点中指定元素
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <returns>元素数组</returns>
        public ArrayList GetRecursiveChildNodesFromParent(XmlNode parentNode, string sElementName)
        {
            ArrayList elementList = new ArrayList();

            try
            {
                XmlNodeList children = parentNode.ChildNodes;
                foreach (XmlNode child in children)
                {
                    if (child.Name == sElementName)
                        elementList.Add(child);

                    if (child.HasChildNodes == true)
                    {
                        ArrayList childrenList = GetRecursiveChildNodesFromParent(child, sElementName);
                        if (childrenList.Count > 0)
                        {
                            foreach (XmlNode subChild in childrenList)
                                elementList.Add(subChild);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return elementList;
        }
        /// <summary>
        /// 为指定的节点创建元素
        /// </summary>
        /// <param name="parentNode">父节点</param>
        /// <param name="sElementName">元素名</param>
        /// <param name="sElementValue">元素值</param>
        /// <returns>元素对象</returns>
        public XmlElement CreateNodeElement(XmlNode parentNode, string sElementName, string sElementValue)
        {
            XmlElement newElem = null;

            try
            {
                newElem = m_xmlDocument.CreateElement(sElementName);
                newElem.InnerXml = Encode(sElementValue);
                XmlDocument ownerDoc = parentNode.OwnerDocument;

                if (ownerDoc != null)
                {
                    parentNode.AppendChild(newElem);
                }
                else
                {
                    XmlElement root = m_xmlDocument.DocumentElement;
                    root.AppendChild(newElem);
                }

            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return newElem;
        }
        /// <summary>
        /// 为指定节点创建注释节点，如果是根节点或者空节点，则直接创建到节点树
        /// </summary>
        /// <param name="insertAfterThisNode">要创建注释的节点</param>
        /// <param name="sVal">注释内容</param>
        /// <returns>节点对象</returns>
        public XmlNode CreateComment(XmlNode insertAfterThisNode, string sVal)
        {
            if (insertAfterThisNode == null)
                return null;

            XmlNode createdNode = null;
            try
            {
                XmlComment commentNode = m_xmlDocument.CreateComment(Encode(sVal));
                createdNode = insertAfterThisNode.AppendChild(commentNode);
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return createdNode;
        }
        /// <summary>
        /// 创建XML标识节点，即XML的文件头
        /// </summary>
        /// <param name="version">版本</param>
        /// <param name="encoding">编码</param>
        /// <param name="standalone">standalone属性，必须是"yes"或者"no"</param>
        /// <returns>标识节点</returns>
        public XmlNode CreateXmlDeclaration(string version, string encoding, string standalone)
        {
            XmlNode createdNode = null;
            try
            {
                XmlDeclaration dec = m_xmlDocument.CreateXmlDeclaration(version, encoding, standalone);
                createdNode = m_xmlDocument.PrependChild(dec);
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return createdNode;
        }

        /// <summary>
        /// 从节点树上删除节点
        /// </summary>
        /// <param name="targetNode">节点</param>
        /// <returns>成功返回True 否则返回False</returns>
        public bool DeleteNodeElement(XmlNode targetNode)
        {
            bool bResult = false;

            try
            {
                XmlNode xmlNode = RootNode.RemoveChild(targetNode);
                if (xmlNode != null)
                    bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bResult;
        }
        /// <summary>
        /// 修改节点的值
        /// </summary>
        /// <param name="targetNode">节点</param>
        /// <param name="sNewElementValue">新值</param>
        /// <returns>成功返回True 否则返回False</returns>
        public bool ModifyNodeElementValue(XmlNode targetNode, string sNewElementValue)
        {
            bool bResult = false;

            try
            {
                targetNode.InnerXml = Encode(sNewElementValue);
                bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return bResult;
        }
        /// <summary>
        /// 为指定节点创建属性
        /// </summary>
        /// <param name="targetElement">节点</param>
        /// <param name="sAttributeName">属性名</param>
        /// <param name="sAttributeValue">属性值</param>
        /// <returns>节点属性</returns>
        public XmlAttribute CreateNodeAttribute(XmlElement targetElement, string sAttributeName, string sAttributeValue)
        {
            XmlAttribute newAttr = null;

            try
            {
                newAttr = m_xmlDocument.CreateAttribute(sAttributeName);
                targetElement.SetAttributeNode(newAttr);
                targetElement.SetAttribute(sAttributeName, "", Encode(sAttributeValue));
            }
            catch (Exception e)
            {
                HandleException(e);
            }

            return newAttr;
        }

        /// <summary>
        /// 删除节点属性
        /// </summary>
        /// <param name="targetNode">节点</param>
        /// <param name="sAttributeName">属性名</param>
        /// <returns>成功返回True 否则返回False</returns>
        public bool DeleteNodeAttribute(XmlNode targetNode, string sAttributeName)
        {
            bool bResult = false;

            try
            {
                XmlAttributeCollection attrColl = targetNode.Attributes;
                XmlAttribute xmlAttribute = attrColl.Remove((XmlAttribute)attrColl[sAttributeName, ""]);
                if (xmlAttribute != null)
                    bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bResult;
        }
        /// <summary>
        /// 将当前XML结构创建到指定的文件
        /// </summary>
        /// <param name="sTargetFile">指定文件</param>
        /// <returns>成功返回True 否则返回False</returns>
        public bool GenerateSchema(string sTargetFile)
        {
            bool bResult = false;
            try
            {
                DataSet data = new System.Data.DataSet();
                data.ReadXml(new XmlNodeReader(RootNode), XmlReadMode.Auto);
                data.WriteXmlSchema(sTargetFile);
                bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bResult;
        }
        /// <summary>
        /// 将当前XML结构转为字符串文本
        /// </summary>
        /// <returns>XML结构文本</returns>
        public string GenerateSchemaAsString()
        {
            string sSchemaXmlString = "";
            try
            {
                DataSet data = new System.Data.DataSet();
                data.ReadXml(new XmlNodeReader(RootNode), XmlReadMode.Auto);

                string sTempFile = Path.GetTempFileName();

                data.WriteXmlSchema(sTempFile);

                // 将数据读入字符串
                StreamReader sr = new StreamReader(sTempFile);
                sSchemaXmlString = sr.ReadToEnd();
                sr.Close();

                if (File.Exists(sTempFile) == true)
                    File.Delete(sTempFile);
            }
            catch (Exception e)
            {
                HandleException(e);
                sSchemaXmlString = "<root><error>" + LastErrorMessage + "</error></root>";
            }
            return sSchemaXmlString;
        }
        /// <summary>
        /// 修改指定节点属性的值
        /// </summary>
        /// <param name="targetNode">节点</param>
        /// <param name="sAttributeName">属性名</param>
        /// <param name="sNewAttributeValue">新的值</param>
        /// <returns>成功返回True 否则返回False</returns>
        public bool ModifyNodeAttributeValue(XmlNode targetNode, string sAttributeName, string sNewAttributeValue)
        {
            bool bResult = false;

            try
            {
                XmlAttributeCollection attrColl = targetNode.Attributes;
                XmlAttribute xmlAttribute = (XmlAttribute)attrColl[sAttributeName, ""];
                xmlAttribute.Value = Encode(sNewAttributeValue);
                bResult = true;
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            return bResult;
        }
        /// <summary>
        /// 为HTML和XML标记编码
        /// </summary>
        /// <param name="input">输入文本</param>
        /// <returns></returns>
        private string Encode(string input)
        {
            string output = input;
            output = Regex.Replace(output, "&", "&");
            output = Regex.Replace(output, "<", "<");
            output = Regex.Replace(output, ">", ">");
            output = Regex.Replace(output, "\"", "\"");
            return output;
        }
        /// <summary>
        /// 为HTML和XML标记解码
        /// </summary>
        /// <param name="input">输入文本</param>
        /// <returns></returns>
        private string Decode(string input)
        {
            string output = input;
            output = Regex.Replace(output, "&", "&");
            output = Regex.Replace(output, "<", "<");
            output = Regex.Replace(output, ">", ">");
            output = Regex.Replace(output, "\"", "\"");
            return output;
        }
        /// <summary>
        /// 异常处理
        /// </summary>
        /// <param name="e">异常对象</param>
        private void HandleException(Exception e)
        {
            CNVP.Framework.Helper.LogHelper.Write("运行异常信息",e.ToString());
        }
    }
}